package com.dpkit.manager.factory;

import com.dpkit.adapter.orm.definition.AssociationDefinition;
import com.dpkit.adapter.orm.definition.EntityDefinition;
import com.dpkit.adapter.orm.definition.FieldDefinition;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.*;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 动态实体类工厂
 * 根据EntityDefinition生成运行时类
 */
public class DynamicEntityFactory {
    private static final Map<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>();
    private final String basePackage;
    private final boolean bytecodeEnhanced;

    public DynamicEntityFactory(String basePackage, boolean bytecodeEnhanced) {
        this.basePackage = StringUtils.isNotBlank(basePackage) ? basePackage : "dynamic.entities";
        this.bytecodeEnhanced = bytecodeEnhanced;
    }

    /**
     * 生成或获取缓存的实体类
     */
    public Class<?> createEntityClass(EntityDefinition definition) {
        String fullClassName = getFullClassName(definition.getEntityName());
        return CLASS_CACHE.computeIfAbsent(fullClassName, key -> {
            try {
                return buildEntityClass(definition);
            } catch (Exception e) {
                throw new RuntimeException("Failed to generate entity class: " + fullClassName, e);
            }
        });
    }

    /**
     * 构建实体类字节码
     */
    private Class<?> buildEntityClass(EntityDefinition definition) throws Exception {
        DynamicType.Builder<?> builder = new ByteBuddy()
            .subclass(Object.class)
            .name(getFullClassName(definition.getEntityName()))
            .annotateType(createEntityAnnotation(definition))
            .annotateType(createTableAnnotation(definition));

        // 添加字段和注解
        for (FieldDefinition field : definition.getFields().values()) {
            builder = addClassField(builder, field);
        }

        // 添加默认构造器
        builder = builder.defineConstructor(Visibility.PUBLIC)
            .withoutParameters()
            .intercept(net.bytebuddy.implementation.FixedValue.nullValue());

        DynamicType.Unloaded<?> unloadedType = builder.make();

        // 加载类
        return unloadedType.load(getClass().getClassLoader(), 
            bytecodeEnhanced ? 
                ClassLoadingStrategy.Default.WRAPPER : 
                ClassLoadingStrategy.Default.INJECTION)
            .getLoaded();
    }

    /**
     * 为类添加字段
     */
    private DynamicType.Builder<?> addClassField(DynamicType.Builder<?> builder, FieldDefinition field) {
        // 基础字段定义
        builder = builder.defineField(
            field.getFieldName(), 
            field.getJavaType(), 
            Visibility.PRIVATE
        );

        // JPA @Column 注解
        builder = builder.annotateField(createColumnAnnotation(field));

        // ID字段特殊处理
        if (field.isId()) {
            builder = builder.annotateField(AnnotationDescription.Builder.ofType(Id.class).build())
                .annotateField(createGeneratedValueAnnotation(field));
        }

        // 关联字段处理
        if (field.isAssociation()) {
            builder = processAssociationField(builder, field);
        }

        return builder;
    }

    /**
     * 处理关联字段注解
     */
    private DynamicType.Builder<?> processAssociationField(DynamicType.Builder<?> builder, FieldDefinition field) {
        AssociationDefinition association = field.getAssociation();
        switch (association.getRelationshipType()) {
            case MANY_TO_ONE:
                return builder.annotateField(
                    AnnotationDescription.Builder.ofType(ManyToOne.class)
                        .define("fetch", association.getFetchType())
                        .define("cascade", association.getCascadeTypes())
                        .build()
                ).annotateField(
                    AnnotationDescription.Builder.ofType(JoinColumn.class)
                        .define("name", association.getJoinColumn())
                        .build()
                );

            case ONE_TO_MANY:
                return builder.annotateField(
                    AnnotationDescription.Builder.ofType(OneToMany.class)
                        .define("fetch", association.getFetchType())
                        .define("cascade", association.getCascadeTypes())
                        .define("mappedBy", association.getMappedBy())
                        .build()
                );

            case MANY_TO_MANY:
                return builder.annotateField(
                    AnnotationDescription.Builder.ofType(ManyToMany.class)
                        .define("fetch", association.getFetchType())
                        .define("cascade", association.getCascadeTypes())
                        .build()
                ).annotateField(
                    createJoinTableAnnotation(association)
                );

            case ONE_TO_ONE:
                return builder.annotateField(
                    AnnotationDescription.Builder.ofType(OneToOne.class)
                        .define("fetch", association.getFetchType())
                        .define("cascade", association.getCascadeTypes())
                        .build()
                );
            default:
                return builder;
        }
    }

    /**
     * 创建实体实例
     */
    public Object createEntityInstance(Class<?> entityClass, Map<String, Object> fieldValues) {
        try {
            Object instance = entityClass.getDeclaredConstructor().newInstance();
            for (Map.Entry<String, Object> entry : fieldValues.entrySet()) {
                setFieldValue(entityClass, instance, entry.getKey(), entry.getValue());
            }
            return instance;
        } catch (Exception e) {
            throw new RuntimeException("Failed to create entity instance", e);
        }
    }

    /**
     * 反射设置字段值
     */
    private void setFieldValue(Class<?> clazz, Object instance, String fieldName, Object value) {
        try {
            java.lang.reflect.Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(instance, value);
        } catch (Exception e) {
            throw new RuntimeException("Failed to set field value: " + fieldName, e);
        }
    }

    // 以下为注解创建辅助方法
    private AnnotationDescription createEntityAnnotation(EntityDefinition definition) {
        return AnnotationDescription.Builder.ofType(Entity.class).build();
    }

    private AnnotationDescription createTableAnnotation(EntityDefinition definition) {
        return AnnotationDescription.Builder.ofType(Table.class)
            .define("name", definition.getTableName())
            .build();
    }

    private AnnotationDescription createColumnAnnotation(FieldDefinition field) {
        return AnnotationDescription.Builder.ofType(Column.class)
            .define("name", field.getColumnName())
            .define("nullable", field.isNullable())
            .define("length", field.getLength())
            .build();
    }

    private AnnotationDescription createGeneratedValueAnnotation(FieldDefinition field) {
        return AnnotationDescription.Builder.ofType(GeneratedValue.class)
            .define("strategy", field.getIdGenerationStrategy().toGenerationType())
            .build();
    }

    private AnnotationDescription createJoinTableAnnotation(AssociationDefinition association) {
        return AnnotationDescription.Builder.ofType(JoinTable.class)
            .define("name", association.getJoinTable().getName())
            .define("joinColumns", new AnnotationDescription[]{
                AnnotationDescription.Builder.ofType(JoinColumn.class)
                    .define("name", association.getJoinTable().getJoinColumn())
                    .build()
            })
            .define("inverseJoinColumns", new AnnotationDescription[]{
                AnnotationDescription.Builder.ofType(JoinColumn.class)
                    .define("name", association.getJoinTable().getInverseJoinColumn())
                    .build()
            })
            .build();
    }

    private String getFullClassName(String simpleName) {
        return basePackage + "." + simpleName;
    }
}